{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "jBasof3bv1LB"
   },
   "source": [
    "<h1><center>How to export 🤗 Transformers Models to ONNX ?<h1><center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[ONNX](http://onnx.ai/) is open format for machine learning models. It allows to save your neural network's computation graph in a framework agnostic way, which might be particulary helpful when deploying deep learning models.\n",
    "\n",
    "Indeed, businesses might have other requirements _(languages, hardware, ...)_ for which the training framework might not be the best suited in inference scenarios. In that context, having a representation of the actual computation graph that can be shared accross various business units and logics across an organization might be a desirable component.\n",
    "\n",
    "Along with the serialization format, ONNX also provides a runtime library which allows efficient and hardware specific execution of the ONNX graph. This is done through the [onnxruntime](https://microsoft.github.io/onnxruntime/) project and already includes collaborations with many hardware vendors to seamlessly deploy models on various platforms.\n",
    "\n",
    "Through this notebook we'll walk you through the process to convert a PyTorch or TensorFlow transformers model to the [ONNX](http://onnx.ai/) and leverage [onnxruntime](https://microsoft.github.io/onnxruntime/) to run inference tasks on models from  🤗 __transformers__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "yNnbrSg-5e1s"
   },
   "source": [
    "## Exporting 🤗 transformers model to ONNX\n",
    "\n",
    "---\n",
    "\n",
    "Exporting models _(either PyTorch or TensorFlow)_ is easily achieved through the conversion tool provided as part of 🤗 __transformers__ repository. \n",
    "\n",
    "Under the hood the process is sensibly the following: \n",
    "\n",
    "1. Allocate the model from transformers (**PyTorch or TensorFlow**)\n",
    "2. Forward dummy inputs through the model this way **ONNX** can record the set of operations executed\n",
    "3. Optionally define dynamic axes on input and output tensors\n",
    "4. Save the graph along with the network parameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "!pip install --upgrade git+https://github.com/huggingface/transformers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "PwAaOchY4N2-"
   },
   "outputs": [],
   "source": [
    "!rm -rf onnx/\n",
    "from transformers.convert_graph_to_onnx import convert\n",
    "\n",
    "# Handles all the above steps for you\n",
    "convert(framework=\"pt\", model=\"bert-base-cased\", output=\"onnx/bert-base-cased.onnx\", opset=11)\n",
    "\n",
    "# Tensorflow \n",
    "# convert(framework=\"tf\", model=\"bert-base-cased\", output=\"onnx/bert-base-cased.onnx\", opset=11)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## How to leverage runtime for inference over an ONNX graph\n",
    "\n",
    "---\n",
    "\n",
    "As mentionned in the introduction, **ONNX** is a serialization format and many side projects can load the saved graph and run the actual computations from it. Here, we'll focus on the official [onnxruntime](https://microsoft.github.io/onnxruntime/). The runtime is implemented in C++ for performance reasons and provides API/Bindings for C++, C, C#, Java and Python.\n",
    "\n",
    "In the case of this notebook, we will use the Python API to highlight how to load a serialized **ONNX** graph and run inference workload on various backends through **onnxruntime**.\n",
    "\n",
    "**onnxruntime** is available on pypi:\n",
    "\n",
    "- onnxruntime: ONNX + MLAS (Microsoft Linear Algebra Subprograms)\n",
    "- onnxruntime-gpu: ONNX + MLAS + CUDA\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "!pip install transformers onnxruntime-gpu onnx psutil matplotlib"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "-gP08tHfBvgY"
   },
   "source": [
    "## Preparing for an Inference Session\n",
    "\n",
    "---\n",
    "\n",
    "Inference is done using a specific backend definition which turns on hardware specific optimizations of the graph. \n",
    "\n",
    "Optimizations are basically of three kinds: \n",
    "\n",
    "- **Constant Folding**: Convert static variables to constants in the graph \n",
    "- **Deadcode Elimination**: Remove nodes never accessed in the graph\n",
    "- **Operator Fusing**: Merge multiple instruction into one (Linear -> ReLU can be fused to be LinearReLU)\n",
    "\n",
    "ONNX Runtime automatically applies most optimizations by setting specific `SessionOptions`.\n",
    "\n",
    "Note:Some of the latest optimizations that are not yet integrated into ONNX Runtime are available in [optimization script](https://github.com/microsoft/onnxruntime/tree/master/onnxruntime/python/tools/transformers) that tunes models for the best performance."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "# # An optional step unless\n",
    "# # you want to get a model with mixed precision for perf accelartion on newer GPU\n",
    "# # or you are working with Tensorflow(tf.keras) models or pytorch models other than bert\n",
    "\n",
    "# !pip install onnxruntime-tools\n",
    "# from onnxruntime_tools import optimizer\n",
    "\n",
    "# # Mixed precision conversion for bert-base-cased model converted from Pytorch\n",
    "# optimized_model = optimizer.optimize_model(\"bert-base-cased.onnx\", model_type='bert', num_heads=12, hidden_size=768)\n",
    "# optimized_model.convert_model_float32_to_float16()\n",
    "# optimized_model.save_model_to_file(\"bert-base-cased.onnx\")\n",
    "\n",
    "# # optimizations for bert-base-cased model converted from Tensorflow(tf.keras)\n",
    "# optimized_model = optimizer.optimize_model(\"bert-base-cased.onnx\", model_type='bert_keras', num_heads=12, hidden_size=768)\n",
    "# optimized_model.save_model_to_file(\"bert-base-cased.onnx\")\n"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "from os import environ\n",
    "from psutil import cpu_count\n",
    "\n",
    "# Constants from the performance optimization available in onnxruntime\n",
    "# It needs to be done before importing onnxruntime\n",
    "environ[\"OMP_NUM_THREADS\"] = str(cpu_count(logical=True))\n",
    "environ[\"OMP_WAIT_POLICY\"] = 'ACTIVE'\n",
    "\n",
    "from onnxruntime import InferenceSession, SessionOptions, get_all_providers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "2k-jHLfdcTFS"
   },
   "outputs": [],
   "source": [
    "def create_model_for_provider(model_path: str, provider: str) -> InferenceSession: \n",
    "  \n",
    "  assert provider in get_all_providers(), f\"provider {provider} not found, {get_all_providers()}\"\n",
    "\n",
    "  # Few properties than might have an impact on performances (provided by MS)\n",
    "  options = SessionOptions()\n",
    "  options.intra_op_num_threads = 1\n",
    "\n",
    "  # Load the model as a graph and prepare the CPU backend \n",
    "  return InferenceSession(model_path, options, providers=[provider])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "teJdG3amE-hR"
   },
   "source": [
    "## Forwarding through our optimized ONNX model running on CPU\n",
    "\n",
    "---\n",
    "\n",
    "When the model is loaded for inference over a specific provider, for instance **CPUExecutionProvider** as above, an optimized graph can be saved. This graph will might include various optimizations, and you might be able to see some **higher-level** operations in the graph _(through [Netron](https://github.com/lutzroeder/Netron) for instance)_ such as:\n",
    "- **EmbedLayerNormalization**\n",
    "- **Attention**\n",
    "- **FastGeLU**\n",
    "\n",
    "These operations are an example of the kind of optimization **onnxruntime** is doing, for instance here gathering multiple operations into bigger one _(Operator Fusing)_."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "colab_type": "code",
    "id": "dmC22kJfVGYe",
    "outputId": "f3aba5dc-15c0-4f82-b38c-1bbae1bf112e"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Sequence output: (1, 6, 768), Pooled output: (1, 768)\n"
     ]
    }
   ],
   "source": [
    "from transformers import BertTokenizerFast\n",
    "\n",
    "tokenizer = BertTokenizerFast.from_pretrained(\"bert-base-cased\")\n",
    "cpu_model = create_model_for_provider(\"onnx/bert-base-cased.onnx\", \"CPUExecutionProvider\")\n",
    "\n",
    "# Inputs are provided through numpy array\n",
    "model_inputs = tokenizer.encode_plus(\"My name is Bert\", return_tensors=\"pt\")\n",
    "inputs_onnx = {k: v.cpu().detach().numpy() for k, v in model_inputs.items()}\n",
    "\n",
    "# Run the model (None = get all the outputs)\n",
    "sequence, pooled = cpu_model.run(None, inputs_onnx)\n",
    "\n",
    "# Print information about outputs\n",
    "\n",
    "print(f\"Sequence output: {sequence.shape}, Pooled output: {pooled.shape}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Kda1e7TkEqNR"
   },
   "source": [
    "## Benchmarking different CPU & GPU providers\n",
    "\n",
    "_**Disclamer: results may vary from the actual hardware used to run the model**_"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 170
    },
    "colab_type": "code",
    "id": "WcdFZCvImVig",
    "outputId": "bfd779a1-0bc7-42db-8587-e52a485ec5e3"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Doing GPU inference on TITAN RTX\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Warming up: 100%|██████████| 10/10 [00:00<00:00, 333.82it/s]\n",
      "Tracking inference time on CUDAExecutionProvider: 100%|██████████| 100/100 [00:00<00:00, 521.76it/s]\n",
      "Warming up: 100%|██████████| 10/10 [00:00<00:00, 62.95it/s]\n",
      "Tracking inference time on CPUExecutionProvider: 100%|██████████| 100/100 [00:01<00:00, 68.65it/s]\n",
      "Warming up: 100%|██████████| 10/10 [00:00<00:00, 69.72it/s]\n",
      "Tracking inference time on TensorrtExecutionProvider: 100%|██████████| 100/100 [00:01<00:00, 71.31it/s]\n",
      "Warming up: 100%|██████████| 10/10 [00:00<00:00, 66.28it/s]\n",
      "Tracking inference time on DnnlExecutionProvider: 100%|██████████| 100/100 [00:01<00:00, 72.03it/s]\n"
     ]
    }
   ],
   "source": [
    "from torch.cuda import get_device_name\n",
    "from contextlib import contextmanager\n",
    "from dataclasses import dataclass\n",
    "from time import time\n",
    "from tqdm import trange\n",
    "\n",
    "print(f\"Doing GPU inference on {get_device_name(0)}\", flush=True)\n",
    "\n",
    "@contextmanager\n",
    "def track_infer_time(buffer: [int]):\n",
    "    start = time()\n",
    "    yield\n",
    "    end = time()\n",
    "\n",
    "    buffer.append(end - start)\n",
    "\n",
    "\n",
    "@dataclass\n",
    "class OnnxInferenceResult:\n",
    "  model_inference_time: [int]  \n",
    "  optimized_model_path: str\n",
    "\n",
    "\n",
    "# All the providers we'll be using in the test\n",
    "results = {}\n",
    "providers = [\n",
    "  \"CUDAExecutionProvider\",\n",
    "  \"CPUExecutionProvider\",            \n",
    "  \"TensorrtExecutionProvider\",\n",
    "  \"DnnlExecutionProvider\",          \n",
    "]\n",
    "\n",
    "# Iterate over all the providers\n",
    "for provider in providers:\n",
    "\n",
    "  # Create the model with the specified provider\n",
    "  model = create_model_for_provider(\"onnx/bert-base-cased.onnx\", provider)\n",
    "\n",
    "  # Keep track of the inference time\n",
    "  time_buffer = []\n",
    "\n",
    "  # Warm up the model\n",
    "  for _ in trange(10, desc=\"Warming up\"):\n",
    "    model.run(None, inputs_onnx)\n",
    "\n",
    "  # Compute \n",
    "  for _ in trange(100, desc=f\"Tracking inference time on {provider}\"):\n",
    "    with track_infer_time(time_buffer):\n",
    "      model.run(None, inputs_onnx)\n",
    "\n",
    "  # Store the result\n",
    "  results[provider] = OnnxInferenceResult(\n",
    "      time_buffer,\n",
    "      model.get_session_options().optimized_model_filepath\n",
    "  )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 51
    },
    "colab_type": "code",
    "id": "PS_49goe197g",
    "outputId": "0ef0f70c-f5a7-46a0-949a-1a93f231d193"
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Warming up: 100%|██████████| 10/10 [00:00<00:00, 18.04it/s]\n",
      "Tracking inference time on PyTorch: 100%|██████████| 100/100 [00:05<00:00, 18.88it/s]\n"
     ]
    }
   ],
   "source": [
    "from transformers import BertModel\n",
    "\n",
    "# Add PyTorch to the providers\n",
    "model_pt = BertModel.from_pretrained(\"bert-base-cased\")\n",
    "for _ in trange(10, desc=\"Warming up\"):\n",
    "  model_pt(**model_inputs)\n",
    "\n",
    "# Compute \n",
    "time_buffer = []\n",
    "for _ in trange(100, desc=f\"Tracking inference time on PyTorch\"):\n",
    "  with track_infer_time(time_buffer):\n",
    "    model_pt(**model_inputs)\n",
    "\n",
    "# Store the result\n",
    "results[\"Pytorch\"] = OnnxInferenceResult(\n",
    "    time_buffer, \n",
    "    model.get_session_options().optimized_model_filepath\n",
    ") "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Show the inference performance of each providers \n",
    "\n",
    "_Note: PyTorch model benchmark is run on CPU_"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 676
    },
    "colab_type": "code",
    "id": "dj-rS8AcqRZQ",
    "outputId": "b4bf07d1-a7b4-4eff-e6bd-d5d424fd17fb"
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABRoAAAPeCAYAAABjjKazAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+j8jraAAAgAElEQVR4nOzdd5SU5d34/88sZZe2gIBABAHBgjWICYIKqFhBJKKAGoOFkMRYwBgjMRGJKIpGUaOiMQ+xrAELEDSPgkY0FuyxRKOiAeVBBUQBqSI73z/87fwYdqnX6oJ5vc7Zc3avuWfua+4pR97eJZPNZrMBAAAAAJCgoKonAAAAAABs+4RGAAAAACCZ0AgAAAAAJBMaAQAAAIBkQiMAAAAAkExoBAAAAACSCY0AAAAAQDKhEQAAAABIJjQCAAAAAMmERgBgm9K6des49dRTt/j+V111Vey0005RrVq1+O53v1t5E/sWefzxxyOTycTjjz9e1VPJs3Tp0th+++2jpKTkG13vhRdeGJ06ddqs+3zb32dl75H77ruvqqdSaf785z9HJpOJ2bNnb3TZ1O8hAPi2EhoBYCt00003RSaT2ey4wYZNmzYtLrjggjjggANi3Lhxcfnll1f1lKrUTTfdFH/+85+rehqb7Lrrrot69erFgAEDvtH1DhkyJF599dWYMmXKJi3vfQYA/LeqXtUTAADKKykpidatW8fzzz8f7777brRr166qp7TVePvtt6OgYMv+X+ljjz0WBQUF8ac//Slq1qxZyTPb9tx0003RuHHjcntmde3aNVasWLFVbaPVq1fHddddF0OHDo1q1ap9o+tu1qxZHHvssXH11VdH7969N7q899m26ZRTTokBAwZEYWFhVU8FALZZ9mgEgK3MrFmz4plnnolrrrkmmjRp8o0fJhoRUVpaGitXrvzG17spCgsLo0aNGlt03/nz50etWrUqNf4sX7680h5ra1FQUBBFRUVbHHS/Dg8++GAsWLAg+vXrVyXr79evXzz11FPxn//8Z6PLVvb7LJvNxooVKyrlsb4Nvq7vp2rVqkVRUVFkMplKf+wN+fLLL+OLL774RtcJAF+Xree/HgGAiPhqb8aGDRtGz5494/jjj88LjatXr47tttsuTjvttHL3W7JkSRQVFcX555+fG1u1alUMHz482rVrF4WFhdGyZcu44IILYtWqVXn3zWQycdZZZ0VJSUnsscceUVhYGA8//HBERFx99dXRpUuXaNSoUdSqVSs6duxY4XnZVqxYEeecc040btw46tWrF7179465c+dGJpOJSy65JG/ZuXPnxumnnx5NmzaNwsLC2GOPPeJ//ud/Nmn7rHtutLLzqj399NNx3nnnRZMmTaJOnTrxgx/8IBYsWJD3HMeNGxfLli2LTCYTmUwm77Dhu+66Kzp27Bi1atWK7bbbLgYMGBBz5szJW3f37t1jzz33jJdeeim6du0atWvXjl//+tdbtK0nT54ce+65Z+75l23vdbfTGWecEd/5zneisLAw2rRpEz/72c/yosSiRYtiyJAh0bJlyygsLIx27drFlVdeGaWlpRvdjm+88UY88cQTue3RvXv3iKj4HI1lz/21116Lbt26Re3ataNdu3a598ITTzwRnTp1ilq1asWuu+4ajz76aIXPZ0tf98mTJ0fr1q2jbdu2eeOnnnpq1K1bNz744IPo1atX1K1bN3bYYYe48cYbIyLi9ddfj0MOOSTq1KkTrVq1irvvvjvv/qtXr44RI0bEzjvvHEVFRdGoUaM48MAD45FHHslbrkePHhER8de//nWD89zQ++zLL7+MSy+9NNq2bRuFhYXRunXr+PWvf13uPdK6devo1atXTJ06Nfbbb7+oVatW3HLLLRtc73PPPRdHHnlk1K9fP2rXrh3dunWLp59+Om+Z999/P84888zYddddo1atWtGoUaM44YQTKjwn4aJFi2Lo0KHRunXrKCwsjBYtWsSPfvSj+OSTT/KWKy0tjcsuuyxatGgRRUVFceihh8a77767wblGRFxyySWRyWTirbfein79+kVxcXE0atQozj333HIRcUPfT//85z/jqKOOiuLi4qhbt24ceuih8eyzz+bu++KLL0Ymk4nbb7+93BymTp0amUwmHnzwwYio+ByN2Ww2Ro4cGS1atIjatWvHwQcfHG+88UaFz2lTPouzZ8+OTCYTV199dYwZMyb3XnjzzTc3us0AYFvg0GkA2MqUlJTEcccdFzVr1owTTzwxbr755njhhRfie9/7XtSoUSN+8IMfxMSJE+OWW27J22Nq8uTJsWrVqtz560pLS6N3797x1FNPxeDBg6N9+/bx+uuvx7XXXhvvvPNOTJ48OW+9jz32WNxzzz1x1llnRePGjaN169YR8dV58Xr37h0nn3xyfPHFFzF+/Pg44YQT4sEHH4yePXvm7n/qqafGPffcE6ecckrsv//+8cQTT+TdXmbevHmx//775+JBkyZN4qGHHoozzjgjlixZEkOGDNmi7Xb22WdHw4YNY/jw4TF79uwYM2ZMnHXWWTFhwoSIiLjzzjvj1ltvjeeffz5uu+22iIjo0qVLRERcdtll8dvf/jb69esXgwYNigULFsQNN9wQXbt2jX/+85/RoEGD3HoWLlwYRx11VAwYMCB++MMfRtOmTTd7Wz/11FMxceLEOPPMM6NevXpx/fXXR9++feODDz6IRo0aRUTEhx9+GN///vdj0aJFMXjw4Nhtt91i7ty5cd9998Xy5cujZs2asXz58ujWrVvMnTs3fvKTn8SOO+4YzzzzTAwbNiw++uijGDNmzHq315gxY+Lss8+OunXrxkUXXRQREU2bNt3gNv7ss8+iV69eMWDAgDjhhBPi5ptvjgEDBkRJSUkMGTIkfvrTn8ZJJ50UV111VRx//PExZ86cqFevXkSkv+7PPPNM7LvvvhXetmbNmjjqqKOia9euMXr06CgpKYmzzjor6tSpExdddFGcfPLJcdxxx8XYsWPjRz/6UXTu3DnatGkTEV8Fr1GjRsWgQYPi+9//fixZsiRefPHFePnll+Owww7LraN+/frRtm3bePrpp2Po0KHrneeG3meDBg2K22+/PY4//vj4xS9+Ec8991yMGjUq/v3vf8ekSZPyHuftt9+OE088MX7yk5/Ej3/849h1113Xu87HHnssjjrqqOjYsWMMHz48CgoKYty4cXHIIYfEk08+Gd///vcjIuKFF16IZ555JgYMGBAtWrSI2bNnx8033xzdu3ePN998M2rXrh0RX11056CDDop///vfcfrpp8e+++4bn3zySUyZMiX+7//+Lxo3bpxb9xVXXBEFBQVx/vnnx+LFi2P06NFx8sknx3PPPbfe+a6tX79+0bp16xg1alQ8++yzcf3118dnn30Wd9xxR7nnuO730xtvvBEHHXRQFBcXxwUXXBA1atSIW265Jbp3754L3/vtt1/stNNOcc8998TAgQPzHnPChAnRsGHDOOKII9Y7v4svvjhGjhwZRx99dBx99NHx8ssvx+GHH15uD8TN/SyOGzcuVq5cGYMHD47CwsLYbrvtNml7AcBWLwsAbDVefPHFbERkH3nkkWw2m82WlpZmW7RokT333HNzy0ydOjUbEdkHHngg775HH310dqeddsr9feedd2YLCgqyTz75ZN5yY8eOzUZE9umnn86NRUS2oKAg+8Ybb5Sb0/Lly/P+/uKLL7J77rln9pBDDsmNvfTSS9mIyA4ZMiRv2VNPPTUbEdnhw4fnxs4444xs8+bNs5988knesgMGDMjWr1+/3PrW1apVq+zAgQNzf48bNy4bEdkePXpkS0tLc+NDhw7NVqtWLbto0aLc2MCBA7N16tTJe7zZs2dnq1Wrlr3sssvyxl9//fVs9erV88a7deuWjYjs2LFj85bd3G1ds2bN7Lvvvpsbe/XVV7MRkb3hhhtyYz/60Y+yBQUF2RdeeKHcNih7npdeemm2Tp062XfeeSfv9gsvvDBbrVq17AcffFDuvmvbY489st26dSs3Pn369GxEZKdPn17uud999925sbfeeiv33nn22Wdz42Xv0XHjxuXGUl731atXZzOZTPYXv/hFudsGDhyYjYjs5Zdfnhv77LPPsrVq1cpmMpns+PHjy8137ffjPvvsk+3Zs+d61722ww8/PNu+ffuNLlfR++yVV17JRkR20KBBeePnn39+NiKyjz32WG6sVatW2YjIPvzwwxtdV2lpaXbnnXfOHnHEEXnv/+XLl2fbtGmTPeyww/LG1jVjxoxsRGTvuOOO3NjFF1+cjYjsxIkTK1xfNvv/v0fat2+fXbVqVe726667LhsR2ddff32D8x4+fHg2IrK9e/fOGz/zzDOzEZF99dVXc2Pr+37q06dPtmbNmtn33nsvN/bhhx9m69Wrl+3atWtubNiwYdkaNWpkP/3009zYqlWrsg0aNMiefvrpubGy75JZs2Zls9lsdv78+dmaNWtme/bsmbdtf/3rX2cjIu97aFM/i7NmzcpGRLa4uDg7f/78DW4jANgWOXQaALYiJSUl0bRp0zj44IMj4qtDBvv37x/jx4+PNWvWRETEIYccEo0bN87tqRfx1Z5mjzzySPTv3z83du+990b79u1jt912i08++ST3c8ghh0RExPTp0/PW3a1bt9h9993LzalWrVp561m8eHEcdNBB8fLLL+fGyw5jPPPMM/Pue/bZZ+f9nc1m4/77749jjjkmstls3ryOOOKIWLx4cd7jbo7BgwfnnVvtoIMOijVr1sT777+/wftNnDgxSktLo1+/fnnzadasWey8887ltlNhYWG5Q9c3d1v36NEj7xDgvffeO4qLi3Pn/ystLY3JkyfHMcccE/vtt1+5OZc9z3vvvTcOOuigaNiwYd56e/ToEWvWrIl//OMfG9tsm6Vu3bp5V3zeddddo0GDBtG+ffu8K6SX/V72fFJf908//TSy2Ww0bNhwvcsMGjQo93uDBg1i1113jTp16uSd07FsvmufZ7FBgwbxxhtvxMyZMzf6/Mu285b43//934iIOO+88/LGf/GLX0RExN/+9re88TZt2mxwT7syr7zySsycOTNOOumkWLhwYW67Llu2LA499ND4xz/+kTt0d+3P8urVq2PhwoXRrl27aNCgQd72v//++2OfffaJH/zgB+XWt+75C0877bS8PasPOuigiIhNOpdlRMTPf/7zvL/LvjPKtleZdb+f1qxZE9OmTYs+ffrETjvtlBtv3rx5nHTSSfHUU0/FkiVLIiKif//+sXr16pg4cWJuuWnTpsWiRYvyvjPX9eijj8YXX3wRZ599dt7zrmjv2839LPbt2zeaNGmy3nUDwLbKodMAsJVYs2ZNjB8/Pg4++OCYNWtWbrxTp07x+9//Pv7+97/H4YcfHtWrV4++ffvG3XffHatWrYrCwsKYOHFirF69Ou8fzTNnzox///vf6/3H7Pz58/P+LjuUdF0PPvhgjBw5Ml555ZW8c8mt/Q/v999/PwoKCso9xrpXy16wYEEsWrQobr311rj11ls3aV6bascdd8z7uyxKffbZZxu838yZMyObzcbOO+9c4e3rXnhmhx12KHeRj83d1uvOtWy+ZXNdsGBBLFmyJPbcc8+Nzv21117b5PWmatGiRbnQVL9+/WjZsmW5sYjIez6V8bpns9kKx4uKisptg/r16693vmu/J373u9/FscceG7vsskvsueeeceSRR8Ypp5wSe++9d4Xr39ILhZR9Rtb9TDRr1iwaNGhQLoiv7/O4rrJAuu5hwWtbvHhxNGzYMFasWBGjRo2KcePGxdy5c/O25+LFi3O/v/fee9G3b99NWv+Wfu7KrPu5a9u2bRQUFJQ7b+S622PBggWxfPnyCg8pb9++fZSWlsacOXNijz32iH322Sd22223mDBhQpxxxhkR8dVh040bN879z4CKlL0m686xSZMm5aL35n4WN/X1BYBtjdAIAFuJxx57LD766KMYP358jB8/vtztJSUlcfjhh0dExIABA+KWW26Jhx56KPr06RP33HNP7LbbbrHPPvvkli8tLY299torrrnmmgrXt24cWntvpzJPPvlk9O7dO7p27Ro33XRTNG/ePGrUqBHjxo0rd1GNTVG2Z9UPf/jD9YaRigLPpqhWrVqF4+uLU2vPKZPJxEMPPVThY9StWzfv74q20+Zu6y2da0XrPeyww+KCCy6o8PZddtllsx5vY9Y37409n9TXfbvttotMJrPeeLWl84qI6Nq1a7z33nvx17/+NaZNmxa33XZbXHvttTF27Ni8vSQjvopna5+fcEtsaqis6H1WkbJte9VVV8V3v/vdCpcpew+fffbZMW7cuBgyZEh07tw56tevH5lMJgYMGLDRiwetT2W9l8usb/ts6vZYn/79+8dll10Wn3zySdSrVy+mTJkSJ554YlSvXjn/HNrcz2Lq8wGArZXQCABbiZKSkth+++1zV8td28SJE2PSpEkxduzYqFWrVnTt2jWaN28eEyZMiAMPPDAee+yx3AU9yrRt2zZeffXVOPTQQ7d4L6z7778/ioqKYurUqVFYWJgbHzduXN5yrVq1itLS0pg1a1be3j/rXn22SZMmUa9evVizZk3uKr5VrW3btpHNZqNNmzZbHOYqY1uvrUmTJlFcXBz/+te/NrrepUuXbvG2rIy5borU17169erRtm3bvD19K1PZldxPO+20WLp0aXTt2jUuueSScqFx1qxZeTF/c5R9RmbOnBnt27fPjc+bNy8WLVoUrVq12qLHLTsEv7i4eKPb9r777ouBAwfG73//+9zYypUrY9GiReUec2Pvvcoyc+bMvL373n333SgtLc1djGp9mjRpErVr146333673G1vvfVWFBQU5AX+/v37x4gRI+L++++Ppk2bxpIlS/JOA1CRstdk5syZeYdnL1iwoFz0Tv0sAsC3hXM0AsBWYMWKFTFx4sTo1atXHH/88eV+zjrrrPj8889jypQpERFRUFAQxx9/fDzwwANx5513xpdfflnuXGP9+vWLuXPnxh//+McK17ds2bKNzqtatWqRyWRy54eMiJg9e3a5qyiXnUvupptuyhu/4YYbyj1e37594/77768wZCxYsGCjc6psxx13XFSrVi1GjBhRbi+sbDYbCxcu3OhjVMa2XltBQUH06dMnHnjggXjxxRfL3V42z379+sWMGTNi6tSp5ZZZtGhRfPnllxtcT506dcpFpq9DZbzunTt3rnBbpFr39a1bt260a9cu7zQBEV8dWvzee+/lriC9uY4++uiIiHJXHy7bC7aiK7Rvio4dO0bbtm3j6quvjqVLl5a7fe1tW61atXLv8RtuuCHv8x3x1fkDX3311XJXwo7Y8j0V12fd/7FS9p1x1FFHbfB+1apVi8MPPzz++te/5h1mPW/evLj77rvjwAMPjOLi4tx4+/btY6+99ooJEybEhAkTonnz5tG1a9cNrqNHjx5Ro0aNuOGGG/Ked0VXc0/9LALAt4U9GgFgKzBlypT4/PPPo3fv3hXevv/++0eTJk2ipKQkFxT79+8fN9xwQwwfPjz22muvvL2kIiJOOeWUuOeee+KnP/1pTJ8+PQ444IBYs2ZNvPXWW3HPPffE1KlTK7zQyNp69uwZ11xzTRx55JFx0kknxfz58+PGG2+Mdu3axWuvvZZbrmPHjtG3b98YM2ZMLFy4MPbff/944okn4p133omI/D3nrrjiipg+fXp06tQpfvzjH8fuu+8en376abz88svx6KOPxqeffrpF23BLtW3bNkaOHBnDhg2L2bNnR58+faJevXoxa9asmDRpUgwePDjOP//8DT5GZWzrdV1++eUxbdq06NatWwwePDjat28fH330Udx7773x1FNPRYMGDeKXv/xlTJkyJXr16hWnnnpqdOzYMZYtWxavv/563HfffTF79uwNHurbsWPHuPnmm2PkyJHRrl272H777Td4zroUqa/7scceG3feeWe88847lXpI+O677x7du3ePjh07xnbbbRcvvvhi3HfffXHWWWflLffoo49GNpuNY489dovWs88++8TAgQPj1ltvjUWLFkW3bt3i+eefj9tvvz369OmTuwDU5iooKIjbbrstjjrqqNhjjz3itNNOix122CHmzp0b06dPj+Li4njggQciIqJXr15x5513Rv369WP33XePGTNmxKOPPhqNGjXKe8xf/vKXcd9998UJJ5wQp59+enTs2DE+/fTTmDJlSowdO3aL9+qsyKxZs6J3795x5JFHxowZM+Kuu+6Kk046aZPWMXLkyHjkkUfiwAMPjDPPPDOqV68et9xyS6xatSpGjx5dbvn+/fvHxRdfHEVFRXHGGWdEQcGG97lo0qRJnH/++TFq1Kjo1atXHH30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a2pqorKyMlavXh1du3Zt63EAAABgm9Fv4sNtPQJ8bRZNObGtR8ib1va1rM86HRHRvXv36N69+xYPBwAAAABsXZx1GgAAAABIJjQCAAAAAMmERgAAAAAgmdAIwFZp7dq1kclkIpPJxNq1a9t6HAAAgK3eFoXGzz//PJ544om46aabYs2aNRERsWTJkqitrc3pcAAAAABAYcj6rNPvvvtuHHfccfHee+9FXV1dHHPMMVFRURFTp06Nurq6uPHGG/MxJwAAAADQjmV9ROP48ePjwAMPjI8++ig6d+7ctPyUU06J2bNn53Q4AAAAAKAwZH1E49NPPx3PPvtsFBcXN1ver1+/+OCDD3I2GAAAAABQOLI+orGhoSHq6+tbLF+8eHFUVFTkZCgAAAAAoLBkHRqPPfbYuO6665o+z2QyUVtbG5dddlmccMIJOR0OAICtmzPEAwBsPbIOjdOmTYtnnnkmqqqq4tNPP43vfve7TS+bnjp1aj5mBAAAoA35owAArZH1ezT26dMnXnnllZg5c2a8+uqrUVtbG+eee2783d/9XbOTwwAAAAAA246sQ2NERMeOHeOMM87I9SwAAAAAQIHaotC4ZMmSqK6ujhUrVkRDQ0Oz6y688MKcDAYAAAAAFI6sQ+Mdd9wR3/ve96K4uDi23377yGQyTddlMhmhEQAAAAC2QVmHxksvvTQmTZoUl1xySXTokPW5ZAAAAACArVDWpXDdunUxatQokREAAAAAaJJ1LTz33HPj7rvvzscsAAAAAECByvql01dffXV8+9vfjkcffTT23Xff6NSpU7Prf/nLX+ZsOAAAAACgMGxRaHzsscdizz33jIhocTIYAAAAAGDbk3VonDZtWtx2220xevToPIwDAAAAABSirN+jsaSkJAYNGpSPWQAAAACAApV1aBw/fnz86le/yscsAAAAAECByvql0y+++GL88Y9/jIceeij23nvvFieDue+++3I2HAAAAABQGLIOjd26dYsRI0bkYxYAAAAAoEBlHRpvv/32fMwBAAAAABSwrN+jEQAAAADgi1p1ROO3vvWtmD17dnTv3j3233//yGQym73tvHnzcjYcAAAAAFAYWhUaTz755CgpKWn6+MtCIwAAAACw7WlVaLzsssuaPp48eXK+ZgEAAAAAClTW79HYv3//WLVqVYvlH3/8cfTv3z8nQwEAAAAAhSXr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xo4dG88//3w8/vjj8dlnn8Wxxx4ba9eubbrNRRddFL///e/j7rvvjjlz5sSSJUtixIgRW7Q9AAAAACA/Mo2NjY3Z3GHHHXeMTz75JE466aQ444wzYujQoVFUVJSTYT788MPo0aNHzJkzJwYPHhyrV6+OHXfcMX73u9/Fd77znYiIePPNN2OvvfaK5557Lg455JCvXGdNTU1UVlbG6tWrm50pG4Ct29q1a5v+EFZbW9t01D3QvthXoTDYV9lS/SY+3NYjwNdm0ZQT23qEvGltX8v6iMalS5fGzJkzI5PJxKmnnho77bRTjB07Np599tmkgSP+94zWERHbbbddRETMnTs3PvvssxgyZEjTbQYMGBC77LJLPPfcc5tcR11dXdTU1DS7AAAAAAD5lXVo7NixY3z729+OGTNmxIoVK+Laa6+NRYsWxZFHHhnf+MY3tniQhoaG+MEPTY/c5gAAIABJREFUfhCDBg2KffbZJyIili1bFsXFxdGtW7dmt+3Zs2csW7Zsk+u5+uqro7KysunSt2/fLZ4JAAAAAGidrEPjxrp06RJDhw6N448/PnbfffdYtGjRFq9r7Nix8frrr8fMmTNTRopLLrkkVq9e3XR5//33k9YHAAAAAHy1jltyp3Xr1sX9998fM2bMiNmzZ0ffvn3j9NNPj3vuuWeLhhg3blw89NBD8dRTT0WfPn2alvfq1SvWr18fH3/8cbOjGpcvXx69evXa5LpKSkqipKRki+YAAAAAALZM1kc0jho1Knr06BEXXXRR9O/fP5588slYuHBhXHHFFTFgwICs1tXY2Bjjxo2L+++/P/74xz/Grrvu2uz6Aw44IDp16hSzZ89uWrZgwYJ477334tBDD812dAAAAAAgT7I+orGoqCj+7d/+bZNnm3799deb3l+xNcaOHRu/+93v4t///d+joqKi6X0XKysro3PnzlFZWRnnnntuXHzxxbHddttF165d44ILLohDDz20VWecBgAAAAC+HlmHxhkzZjT7fM2aNXHXXXfFLbfcEnPnzo36+vpWr+vXv/51REQcccQRzZbffvvtMXr06IiIuPbaa6NDhw4xcuTIqKuri6FDh8YNN9yQ7diQM2vXro3y8vKIiKitrY2ysrI2nggAAACg7W3RezRGRDz11FNx6623xr333hu9e/eOESNGxPXXX5/VOhobG7/yNqWlpXH9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2Sev24EvSvk/nj8s0945z/k2atGiRTFo0KDo1q1b/OIXv4h99903Pvvss3jsscdi7Nix8eabb2a1vjFjxjQ9MS5evDjuv//+GDVqVIwePTpuvvnmZretrq6OTz75JL7zne/Ev/7rv8ZPfvKTza5vY126dMn+C/2a7bjjjnlZ73HHHRe333571NXVxR/+8IcYO3ZsdOrUKS655JIWt12/fn0UFxcnb7Nz587RuXPn5PV8lc8++yw6deqU9+1sbVq7D19++eUxZsyYqKmpiWnTpsVpp50WO++8cxx22GFZba9r166xYMGCZssymUzOvp58KS8vj/Ly8ryse8GCBVFRURFvvfVWnHfeeTFs2LB49dVXo6ioqNntGhsbo76+Pjp2TP+R26tXr+R1fJVcPYdsLZYuXdr08axZs2LSpEnN9oV8Pb621Kb+/TY8Br/MhueKjVVUVOR8vlzL1z6x4feQdevWxW9+85sYO3ZsdO/ePU4//fQWt83VPpPP56sNcvl8tLXY8DtWfX19LF++PB599NEYP3583HPPPfHggw/m9Xu19957xxNPPNFsWSH821RWVuZlvRt+12hoaIhXXnklzj777FiyZEk89thjLW5bX1/fFCVTFBUV+dlKm8nm/3j54v9i5IMjGrdR//iP/xiZTCZefPHFGDlyZOyxxx6x9957x8UXXxzPP/981uvr0qVL9OrVK/r06ROHHHJITJ06NW666aaYPn16i1+gbr311vjud78bZ555Ztx2221fur6NL127do2IiN/85jdRXl4eb731VrOvZ8CAAbFu3bqIiHj99dfj+OOPj/Ly8ujZs2eceeaZsXLlyqbbNzQ0xDXXXBO77bZblJSUxC677BI///nPI2LTL494+eWXI5PJxKJFi+LJJ5+Ms88+O1avXt3iiJYvvnT6vffei5NPPjnKy8uja9euceqpp8by5cubrt9whMadd94Z/fr1i8rKyhg1alSsWbOm2fejpKQkevXqFX/1V38V559/fgwZMiQefPDBiPj/o9J+/vOfR+/evWPPPfeMiIjXXnstjjrqqOjcuXNsv/32cd5550VtbW1ERPzHf/xHlJaWtngJyPjx4+Ooo46KiE0ftTllypTo2bNnVFRUxLnnnhuffvppi3+7W265Jfbaa68oLS2NAQMGNDsKZMNf32bNmhWHH354lJaWxowZM1qs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\n",
      "text/plain": [
       "<Figure size 1600x1200 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%matplotlib inline\n",
    "\n",
    "import matplotlib\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import os\n",
    "\n",
    "# Compute average inference time + std\n",
    "time_results = {k: np.mean(v.model_inference_time) * 1e3 for k, v in results.items()}\n",
    "time_results_std = np.std([v.model_inference_time for v in results.values()]) * 1000\n",
    "\n",
    "plt.rcdefaults()\n",
    "fig, ax = plt.subplots(figsize=(16, 12))\n",
    "ax.set_ylabel(\"Avg Inference time (ms)\")\n",
    "ax.set_title(\"Average inference time (ms) for each provider\")\n",
    "ax.bar(time_results.keys(), time_results.values(), yerr=time_results_std)\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "accelerator": "GPU",
  "colab": {
   "collapsed_sections": [],
   "name": "ONNX Overview",
   "provenance": [],
   "toc_visible": true
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}